# 导入蓝图对象
import re
from . import passport_blue
# 导入flask内置的对象
from flask import request,jsonify,current_app,make_response,session
# 导入自定义的状态码
from info.utils.response_code import RET
# 导入captcha工具
from info.utils.captcha.captcha import captcha
# 导入redis对象
from info import redis_store,constants,db
# 导入随机数模块
import random
# 导入云通讯扩展
from info.libs.yuntongxun import sms
# 导入User模型类
from info.models import User
# 导入时间模块
from datetime import datetime


@passport_blue.route('/image_code')
def generate_image_code():
    """
    生成图片验证码
    １、获取前段传入的参数uuid,里面包含着图片的
    ２、判断参数是否存在
    ３、调用captcha扩展生成图片验证码，name,text,image
    ４、根据uuid来存储图片验证码的字符串，text,redis数据库，有效期
    ５、返回图片给浏览器，使用make_response返回本身
    """
    # 获取参数 获取的是前端传过来的,相当于imageCodeId,而imageCodeId就是十六进制的uuid
    image_code_id = request.args.get('image_code_id')
    # 检验参数，接收的uuid是否有值
    if not image_code_id:
        # uuid没有值,返回json数据，里面包含着状态信息和弹出警示框内容
        return jsonify(errno=RET.PARAMERR, errmsg='参数缺失')
    # 调用captcha生成图片验证码
    # name:十六进制的字符串
    # text:随机验证码字符串
    # image：二进制的字符串
    name, text, image = captcha.generate_captcha()
    # print(name,text,image)
    # 把图片验证码的字符串存在redis数据库中
    try:
        # setex:设置键值及过期时间，以秒为单位　
        # constants.IMAGE_CODE_REDIS_EXPIRES　设置过期时间（默认五分钟）
        # 'ImageCode_' + image_code_id 设置的键
        # text　设置的值
        redis_store.setex('ImageCode_' + image_code_id, constants.IMAGE_CODE_REDIS_EXPIRES,text)
    except Exception as e:
        # 使用current_app记录错误信息
        # 如果应用程序是在调试模式,current_app.logger.error(e)记录日志消息
        current_app.logger.error(e)
        # 当链接数据库失败了
        return jsonify(errno=RET.DBERR, errmsg='保存图片验证码失败')
    else:
        # 调用make_response返回验证码图片
        response = make_response(image)
        # 响应的头信息默认是text/html，当前返回的是图片，需要修改默认的响应头
        response.headers['Content-Type'] = 'image/jpg'
        return response


@passport_blue.route('/sms_code',methods=['POST'])
def send_sms_code():
    """
    发送短信，post请求，var params = {前端对象}
    1、获取参数，mobile/image_code/image_code_id
    request.json/request.get_json()/request.form表单
    2、校验参数，检查参数的完整性
    3、检查手机号的格式，使用正则
    4、检查验证码是否正确，从redis中获取服务器存储的真实的图片验证码image_cd = redisd_store.get('imagecode')
    5、判断从redis中获取结果是否有数据
    6、如果为空，删除图片验证码
    7、比较图片验证码是否一致
    8、生成短信随机码，random.randint()
    9、保存到redis数据库中
    10、使用云通讯接口发送短信
    11、保存发送结果
    12、判断发送是否成功
    13、返回结果
    :return:
    """
    # 获取post请求的参数
    mobile = request.json.get('mobile')
    image_code = request.json.get('image_code')
    image_code_id = request.json.get('image_code_id')
    # 检查参数的完整性
    if not all([mobile,image_code,image_code_id]):
        return jsonify(errno=RET.PARAMERR,errmsg='参数不完整')
    # 检查手机号的格式
    if not re.match(r'^1[3456789]\d{9}$',mobile):
        return jsonify(errno=RET.PARAMERR,errmsg='手机号格式错误')
    # 从本地redis中获取真实的图片验证码
    try:
        real_image_code = redis_store.get("ImageCode_" + image_code_id)
    except Exception as e:
        current_app.logger.error(e)
        return jsonify(errno=RET.DBERR, errmsg='获取图片验证码数据失败')
    # 判断获取结果是否有数据
    if not real_image_code:
        return jsonify(errno=RET.NODATA,errmsg='图片验证码已过期')
    # 删除图片验证码,因为图片验证码只能比较一次，本质是只能读取redis数据库一次
    try:
        redis_store.delete('ImageCode_' + image_code_id)
    except Exception as e:
        current_app.logger.error(e)
    # 比较图片验证码是否一致,统一转成小写
    if real_image_code.lower() != image_code.lower():
        return jsonify(errno=RET.DATAERR, errmsg='图片验证码不一致')
    # 图片验证码正确后，验收手机号是否注册过
    # 本质：查询数据库，该手机号在数据库中是否存在！？
    try:
        user = User.query.filter_by(mobile=mobile).first()
    except Exception as e:
        current_app.logger.error(e)
        return jsonify(errno=RET.DBERR,errmsg='查询数据失败')
    else:
        # 判断查询结果是否存在
        if user is not None:
            return jsonify(errno=RET.DATAEXIST,errmsg='手机号已注册')

    # 生成短信随机数
    sms_code = '%06d' % random.randint(0, 999999)
    # 保存到redis中
    try:
        redis_store.setex('SMSCode_' + mobile,constants.SMS_CODE_REDIS_EXPIRES,sms_code)
    except Exception as e:
        current_app.logger.error(e)
        return jsonify(errno=RET.DBERR,errmsg='保存短信随机数失败')
    # 调用第三方扩展，云通讯，发送短信
    try:
        ccp = sms.CCP()
        result = ccp.send_template_sms(mobile,[sms_code,constants.SMS_CODE_REDIS_EXPIRES/60],1)
    except Exception as e:
        current_app.logger.error(e)
        return jsonify(errno=RET.THIRDERR,errmsg='发送短信异常')
    # 判断发送结果
    if result == 0:
        return jsonify(errno=RET.OK,errmsg='发送成功')
    else:
        return jsonify(errno=RET.THIRDERR,errmsg='发送失败')


@passport_blue.route('/register',methods=['POST'])
def register():
    """
    用户注册
    １、获取参数，mobile, smscode, password
    ２、检测参数的完整性
    ３、检查手机格式是否正确
    ４、检查手机号码是否存在数据库中，即是否已注册
    ５、检测短信验证码是否一致
    ５．１　先从redis数据库里面取出真实的短信验证码
    ５．２　判断获取结果是否存在
    ５．３　将输入的短信验证码转换成字符串
    ５．４　与数据库里的短信验证码字符串进行对比
    ５．５　删除数据库里的短信验证码
    ６、创建模型类对象，保存数据
    ７、对密码进行加密
    ８、提交数据到mysql数据库中
    ９、把用户信息缓存在redis中
    １０、返回结果
    :return:
    """
    # １、获取post请求的参数信息，mobile,smscode,password
    mobile = request.json.get('mobile')
    sms_code = request.json.get('smscode')
    password = request.json.get('password')
    # ２、检查参数的完整性,即判断参数是否有空值
    if not all([mobile, sms_code, password]):
        return jsonify(errno=RET.PARAMERR, errmsg='参数缺失')
    # ３、检查参数的格式
    if not re.match(r'1[3456789]\d{9}$', mobile):
        return jsonify(errno=RET.PARAMERR, errmsg='手机号格式错误')
    # ４、检查手机号是否注册过
    try:
        user = User.query.filter(User.mobile == mobile).first()
    except Exception as e:
        current_app.logger.error(e)
        return jsonify(errno=RET.DBERR, errmsg='查询用户数据失败')
    else:
        # 判断查询结果如果存在
        if user:
            return jsonify(errno=RET.DATAEXIST, errmsg='手机号已注册')
    # ５、尝试从redis中获取真实的短信验证码，检查验证码是否过期
    try:
        # 获取验证码
        real_sms_code = redis_store.get('SMSCode_' + mobile)
    except Exception as e:
        current_app.logger.error(e)
        return jsonify(errno=RET.DBERR, errmsg='查询短信验证码失败')
    # ６、判断查询的验证码是否是否过期
    if not real_sms_code:
        return jsonify(errno=RET.NODATA, errmsg='短信已过期')
    # ７、比较短信验证码是否正确，注意获取页面的验证码是json数据需要转化成字符串来与数据库的字符串验证码进行对比
    if real_sms_code != str(sms_code):
        return jsonify(errno=RET.DATAERR, errmsg='短信验证码错误')
    # ８、删除数据库里面已比较过的真实短信验证码
    try:
        redis_store.delete('SMSCode_' + mobile)
    except Exception as e:
        current_app.logger.error(e)
    # ９、构造模型类对象,实现用户信息的存储，密码的加密
    user = User()
    user.mobile = mobile
    user.nick_name = mobile
    # 调用了werkzegu的密码加密模块，generate_password_hash方法，实现了密码的sha256加密
    user.password = password
    # １０、提交用户数据到数据库中
    try:
        db.session.add(user)
        db.session.commit()
    except Exception as e:
        current_app.logger.error(e)
        # 提交数据如果发生异常，需要进行回滚
        db.session.rollback()
        return jsonify(errno=RET.DBERR, errmsg='保存数据失败')
    # １１、缓存用户信息到redis数据库中
    session['user_id'] = user.id
    session['mobile'] = mobile
    session['nick_name'] = mobile
    # １２、返回结果
    return jsonify(errno=RET.OK, errmsg='OK')


@passport_blue.route('/login',methods=['POST'])
def login():
    """
    用户登陆：获取参数－－－》参数校验－－－》业务处理－－－》返回结果
    １、获取参数
    ２、校验参数的完整性
    ３、检查手机号码格式
    ４、查询数据库里是否已注册
    ５、判断查询结果
    ６、判断密码是否正确
    ７、提交用户最后登陆时间到mysql中
    ８、缓存用户信息
    ９、返回结果
    :return:
    """
    # １、获取参数
    mobile = request.json.get('mobile')
    password = request.json.get('password')
    # ２、判断参数的完整性
    if not all([mobile,password]):
        return jsonify(errno=RET.PARAMERR, errmsg='参数缺失')
    # ３、检查手机号码的格式
    if not re.match(r'1[3456789][0-9]{9}', mobile):
        return jsonify(errno=RET.PARAMERR, errmsg='手机号码格式错误')
    # 4、查询数据库判断用户是否已注册
    try:
        user = User.query.filter_by(mobile=mobile).first()
    except Exception as e:
        current_app.logger.error(e)
        return jsonify(errno=RET.DBERR, errmsg='查询数据失败')
    # ５、判断查询结果
    if user is None or not user.check_password(password):
        return jsonify(errno=RET.DATAERR,errmsg='用户名或密码错误')
    # ６、保存用户登陆时间
    user.last_login = datetime.now()
    # 提交用户最后的登陆时间
    try:
        db.session.add(user)
        db.session.commit()
    except Exception as e:
        current_app.logger.error(e)
        db.session.rollback()
        return jsonify(errno=RET.DATAERR,errmsg='提交数据失败')
    # ７、缓存用户信息
    session['user_id'] = user.id
    session['mobile'] = user.mobile
    # 如果用户在某一次登录的时候，修改了昵称，让昵称为修改后的结果，如果不修改，默认为手机号；
    session['nick_name'] = user.nick_name
    # ８、返回结果
    return jsonify(errno=RET.OK, errmsg='OK')


@passport_blue.route('/logout')
def logout():
    """
    退出
    session.clear()
    """
    # 把redis中存储的用户信息清除
    session.pop('user_id',None)
    session.pop('mobile',None)
    session.pop('nick_name',None)
    return jsonify(errno=RET.OK,errmsg='OK')



